import os
import subprocess
from typing import List, Optional


def run_cugr_router(bin_path, lef_file, def_file, output_file, threads, rrr_iters, cgmap):
    # pre-validate binary on Windows to avoid WinError 193 (non-Win32 executable)
    cmd = [
        str(bin_path),
        '-lef', str(lef_file),
        '-def', str(def_file),
        '-output', str(output_file),
        '-threads', str(threads),
        '-rrrIters', str(rrr_iters),
        '-cgmap', str(cgmap)
    ]
    print(f"[info] 调用路由器: {' '.join(cmd)}")
    subprocess.run(cmd, capture_output=True, text=True)


def parse_cugr_congestion(input_path, via_coeff):
    num_layers = 0
    gcell_x = 0
    gcell_y = 0
    supply_layers: List[List[List[float]]] = []
    demand_layers: List[List[List[float]]] = []

    with open(input_path, 'r', encoding='utf-8', errors='ignore') as f:
        lines = f.readlines()
        # 解析头部
        body_start_idx = 0
        for idx, line in enumerate(lines):
            if line and line[0] == '#':
                parts = line.strip().split()
                if len(parts) >= 3 and parts[1] == 'layer:':
                    num_layers = int(parts[2])
                elif len(parts) >= 3 and parts[1] == 'gcell(x):':
                    gcell_x = int(parts[2])
                elif len(parts) >= 3 and parts[1] == 'gcell(y):':
                    gcell_y = int(parts[2])
                    supply_layers = [
                        [[0.0 for _ in range(gcell_y)] for _ in range(gcell_x)]
                        for _ in range(num_layers)
                    ]
                    demand_layers = [
                        [[0.0 for _ in range(gcell_y)] for _ in range(gcell_x)]
                        for _ in range(num_layers)
                    ]
            else:
                body_start_idx = idx
                break

        # 解析主体：lineCounter 控制 x 与 z 的推进
        # z:层；x:行；y:列
        lineCounter = 0
        for line in lines[body_start_idx:]:
            line = line.strip()
            if not line:
                continue
            x_index = lineCounter % gcell_x
            z_index = lineCounter // gcell_x
            line = line.replace(", ", ",")
            tuples = line.split(" ")
            y_index = 0
            for item in tuples:
                item = item.replace("(", "").replace(")", "")
                parts = item.split(",")
                if not parts:
                    continue
                try:
                    supply_val = float(parts[0])
                    demand_val = float(parts[1]) if len(parts) > 1 else 0.0
                    pin_val = float(parts[2]) if len(parts) > 2 else 0.0
                    if via_coeff and pin_val > 0.0:
                        demand_val += float(via_coeff) * (pin_val ** 0.5)
                except Exception:
                    supply_val = 0.0
                    demand_val = 0.0
                if 0 <= z_index < num_layers and 0 <= x_index < gcell_x and 0 <= y_index < gcell_y:
                    supply_layers[z_index][x_index][y_index] = supply_val
                    demand_layers[z_index][x_index][y_index] = demand_val
                y_index += 1
            lineCounter += 1

    return {
        'num_layers': num_layers,
        'gcell_x': gcell_x,
        'gcell_y': gcell_y,
        'supply_layers': supply_layers,
        'demand_layers': demand_layers,
    }


def write_gcell_report(supplyH, supplyV, demandH, demandV, gcell_x, gcell_y, output_prob_path):
    with open(output_prob_path, 'w', encoding='utf-8') as ofs:
        ofs.write(f"# layer: 2\n")
        ofs.write(f"# gcell(x): {gcell_x}\n")
        ofs.write(f"# gcell(y): {gcell_y}\n")
        for z in range(2):
            for ix in range(gcell_x):
                row_items: List[str] = []
                for iy in range(gcell_y):
                    if z == 0:
                        s = supplyH[ix][iy]
                        d = demandH[ix][iy]
                    else:
                        s = supplyV[ix][iy]
                        d = demandV[ix][iy]
                    row_items.append(f"{s:.6f},{d:.6f}")
                ofs.write(" ".join(row_items))
                if ix != gcell_x - 1:
                    ofs.write("\t")
            ofs.write("\n")


def congestion_from_cugr(cf):
    # 1.读取拥塞文件
    parsed = parse_cugr_congestion(cf.CUGR_CONGESTION_TXT, cf.VIA_COEFF)
    num_layers = parsed['num_layers']
    gcell_x = parsed['gcell_x']
    gcell_y = parsed['gcell_y']
    supply_layers: List[List[List[float]]] = parsed['supply_layers']
    demand_layers: List[List[List[float]]] = parsed['demand_layers']

    # 2.计算拥塞
    supplyH = [[0.0 for _ in range(gcell_y)] for _ in range(gcell_x)]
    supplyV = [[0.0 for _ in range(gcell_y)] for _ in range(gcell_x)]
    demandH = [[0.0 for _ in range(gcell_y)] for _ in range(gcell_x)]
    demandV = [[0.0 for _ in range(gcell_y)] for _ in range(gcell_x)]
    even_count = (num_layers + 1) // 2
    for z in range(num_layers):
        is_horizontal = (z % 2 == 0)
        targetS = supplyH if is_horizontal else supplyV
        targetD = demandH if is_horizontal else demandV
        layerS = supply_layers[z]
        layerD = demand_layers[z]
        for x in range(gcell_x):
            for y in range(gcell_y):
                targetS[x][y] += layerS[x][y]
                targetD[x][y] += layerD[x][y]

    # Utilization and stats (util indexed as [iy][ix] for consistency)
    utilH = [[(demandH[ix][iy] / supplyH[ix][iy]) if supplyH[ix][iy] > 0 else 0.0
              for ix in range(gcell_x)] for iy in range(gcell_y)]
    utilV = [[(demandV[ix][iy] / supplyV[ix][iy]) if supplyV[ix][iy] > 0 else 0.0
              for ix in range(gcell_x)] for iy in range(gcell_y)]

    flatH = [u for row in utilH for u in row]
    flatV = [u for row in utilV for u in row]
    maxH = max(flatH) if flatH else 0.0
    maxV = max(flatV) if flatV else 0.0
    hotH = sum(1 for u in flatH if u > 1.0)
    hotV = sum(1 for u in flatV if u > 1.0)
    total_cells = gcell_x * gcell_y

    print(f"[cugr->prob] layers={num_layers} grid={gcell_x}x{gcell_y} via_coeff={cf.VIA_COEFF} grouping=even->H, odd->V h_even={even_count}")
    print(f"[cugr->prob] max_util H={maxH:.3f} V={maxV:.3f}")
    print(f"[cugr->prob] overflow cells H={hotH}/{total_cells} V={hotV}/{total_cells}")

    # 3.写出prob报告（仅使用新命名）
    out_prob = cf.PROB_CONGESTION_TXT
    write_gcell_report(supplyH, supplyV, demandH, demandV, gcell_x, gcell_y, out_prob)
